package mdp;

import java.util.Iterator;
import java.util.Vector;

import main.Main;
import env.City;
import env.Road;
import env.World;

public class State
{
	public City				agent;				// in this state's world
	public City				chem;				// in this state's world
	public City				army;				// in this state's world
	public World			world;
	public Action			bestAction;
	public Vector<Action>	possibleActions;
	public boolean			reachable;
	public boolean			initial;
	public boolean			finalState;		// possibly finite, becuase unreachable states are also tagged
	private boolean			inited;
	public Double			utility;

	public State(City agent, City chem, City army, World world)
	{
		this.agent = agent;
		this.chem = chem;
		this.army = army;
		this.world = world;
		this.bestAction = null;
		this.possibleActions = new Vector<Action>();
		this.reachable = false;
		this.initial = false;
		this.finalState = false;
		this.inited = false;
		this.utility = Double.MAX_VALUE;
	}

	public void init()
	{
		initInitial();
		initFinalState();
	}

	private void initInitial()
	{
		initial = agent.name.equals(Main.initialAgentCity)
				&& chem.name.equals(Main.initialChemCity)
				&& army.name.equals(Main.initialArmyCity);
		if (!initial)
			return;
		Iterator<Road> rIt = world.roads.iterator();
		while (rIt.hasNext())
		{
			Road road = (Road) rIt.next();
			if (road.nearCity(agent))
			{
				if (road.blocked == -1
						|| ((road.blocked == 1 && road.tChance == 0.0) || (road.blocked == 0 && road.tChance == 1.0)))
				{
					initial = false;
					return;
				}
			} else
			{
				if (road.blocked != -1)
				{
					initial = false;
					return;
				}
			}
		}
		if (initial)
			reachable = true;
	}

	private void initFinalState()
	{
		if (agent.name.equals(Main.goalCity) && chem.name.equals(Main.goalCity))
		{
			finalState = true;
			utility = 0.0 - Main.goalScore;
		}
	}

	public String output()
	{
		String ans = "";
		ans += "Agent: " + agent.name + ", Chem: " + chem.name + ", Army: "
				+ army.name + "\n";
		ans += "utility: " + utility + "\n";
//		ans += reachable ? "reachable\n" : "unreachable\n";
		if (bestAction != null)
		{
			ans += bestAction.output();
		} else
		{
			if (finalState && reachable)
				ans += "final state\n";
		}
		if (initial)
			ans += "initial state\n";
		ans += world.output();
		return ans;
	}

	@Override
	public String toString()
	{
		String ans = "";
		ans += "Agent: " + agent.name + ", Chem: " + chem.name + ", Army: "
				+ army.name + "\n";
		ans += reachable ? "reachable\n" : "unreachable\n";
		if (bestAction != null)
		{
			ans += "utility: " + utility + "\n";
			ans += bestAction.output();
		} else
		{
			if (finalState && reachable)
				ans += "final state\n";
		}
		if (initial)
			ans += "initial state\n";
		ans += world.output();
		return ans;
	}

	public boolean equalRoadState(int[] roadsState)
	{
		for (int i = 0; i < roadsState.length; i++)
		{
			if (world.roads.elementAt(i).blocked != roadsState[i])
				return false;
		}
		return true;
	}

	public void test()
	{
		Action a = new Action(this, agent, world.getCityByName("2"),
				world.getRoadBetweenCities(agent, world.getCityByName("2")),
				false, false);
		a.connect();
	}

	public void generateActions()
	{
		if (finalState || inited)
			return;
		inited = true;
		Iterator<Road> rIt = agent.getRoads().iterator();
		while (rIt.hasNext())
		{
			Road road = (Road) rIt.next();
			City to = road.getNeighbor(agent);// TODO supposed to work, cause its the same object meaning the same pointer
			Action a = new Action(this, agent, to, road, false, false);
			a.connect();
			bestAction = a;
			possibleActions.add(a);

			if (chem.nameEquals(agent) && road.blocked == 0)
			{
				a = new Action(this, agent, to, road, true, false);
				a.connect();
				possibleActions.add(a);
			}

			if (army.nameEquals(agent))
			{
				a = new Action(this, agent, to, road, false, true);
				a.connect();
				possibleActions.add(a);
			}

			if (chem.nameEquals(agent) && army.nameEquals(agent))
			{
				a = new Action(this, agent, to, road, true, true);
				a.connect();
				possibleActions.add(a);
			}
		}

	}

	public void calculateUtility()
	{
		Iterator<Action> aIt = possibleActions.iterator();
		while (aIt.hasNext())
		{
			Action a = (Action) aIt.next();
			Double currActionUtility = a.calculateUtility();
			if (currActionUtility < utility)
			{
				utility = currActionUtility;
				bestAction = a;
			}
		}
	}

}
